﻿using System;
using System.Text;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using CashFlow.BusinessComponents;
using CashFlow.Entities;
using Moq;
using CashFlow.Repository;
using Should.Fluent;

namespace CashFlow.BusinessComponents.Tests
{
	[TestClass]
	public class CashFlowComponentTest
	{

		private CashFlowComponent component;

		[TestInitialize]
		public void InitTest()
		{
			component = new CashFlowComponent("1");
		}

		/// <summary>
		///A test for GetMonthCashFlow
		///</summary>
		[TestMethod()]
		public void GetMonthCashFlowTest()
		{
			var tenantId = "1";
			var fakeTtenantRepository = new Mock<ITenantRepository>();
			fakeTtenantRepository.
				Setup(o => o.GetById()).
				Returns(new Tenant
				{
					Id = tenantId,
					InitialBalanceDate = new DateTime(2010, 10, 5),
					InitialBalanceAmount = 5000d
				});
			var fakeRecs = GetRecurrences();
			var fakeBillRepository = new Mock<IBillRepository>();
			fakeBillRepository.
				Setup(o => o.GetAll()).
				Returns(new List<Bill>
				{
					new Bill {FirstDate = new DateTime(2010, 9, 3), Recurrence = fakeRecs[0], Amount = 20},
					new Bill {FirstDate = new DateTime(2010, 10, 6), Recurrence = fakeRecs[1], Amount = 30},
					new Bill {FirstDate = new DateTime(2010, 10, 10), Recurrence = fakeRecs[2], Amount = 40},
					new Bill {FirstDate = new DateTime(2010, 10, 23), Recurrence = fakeRecs[3], Amount = 50},
					new Bill {FirstDate = new DateTime(2010, 10, 13), Recurrence = fakeRecs[4], Amount = 60}
				});
			var component = new CashFlowComponent(
				tenantId, fakeTtenantRepository.Object, fakeBillRepository.Object);

			var monthDate = new DateTime(2010, 10, 1);
			var cashFlow = component.GetMonthCashFlow(
				monthDate, 
				fakeBillRepository.Object.GetAll());

			cashFlow.MonthDate.Should().Equal(monthDate);
			cashFlow.Items.Count().Should().Equal(8);
			var item = cashFlow.Items.ElementAt(0);
			item.Bill.Amount.Should().Equal(60d);
			item.Date.Should().Equal(new DateTime(2010, 10, 25));
			item = cashFlow.Items.ElementAt(1);
			item.Bill.Amount.Should().Equal(50d);
			item.Date.Should().Equal(new DateTime(2010, 10, 21));
			item = cashFlow.Items.ElementAt(2);
			item.Bill.Amount.Should().Equal(50d);
			item.Date.Should().Equal(new DateTime(2010, 10, 20));
		}

		[TestMethod]
		public void CalcBalance_InitialBalance_On_1st_Day()
		{
			var items = GetCashFlowItems();
			var monthCashFlow = new MonthCashFlow(new DateTime(2010, 10, 1))
			{
				Items = items
			};

			component.CalcBalances(monthCashFlow, new DateTime(2010, 10, 1), 100);

			items[0].Balance.Should().Equal(110d);
			items[1].Balance.Should().Equal(130d);
			items[2].Balance.Should().Equal(160d);
			items[3].Balance.Should().Equal(200d);
			items[4].Balance.Should().Equal(250d);
			items[5].Balance.Should().Equal(310d);
			items[6].Balance.Should().Equal(380d);
			monthCashFlow.BeginingBalance.Should().Equal(100d);
			monthCashFlow.EndingBalance.Should().Equal(380d);
		}

		[TestMethod]
		public void CalcBalance_InitialBalance_On_7th_Day()
		{
			var items = GetCashFlowItems();
			var monthCashFlow = new MonthCashFlow(new DateTime(2010, 10, 1))
			{
				Items = items
			};
			var component = new CashFlowComponent("1");

			component.CalcBalances(monthCashFlow, new DateTime(2010, 10, 7), 500);

			items[0].Balance.Should().Equal(400d);
			items[1].Balance.Should().Equal(410d);
			items[2].Balance.Should().Equal(430d);
			items[3].Balance.Should().Equal(460d);
			items[4].Balance.Should().Equal(550d);	
			items[5].Balance.Should().Equal(610d);
			items[6].Balance.Should().Equal(680d);
			monthCashFlow.BeginingBalance.Should().Equal(400d);
			monthCashFlow.EndingBalance.Should().Equal(680d);
		}

		[TestMethod]
		public void CalcBalance_InitialBalance_On_Last_Day()
		{
			var items = GetCashFlowItems();
			var monthCashFlow = new MonthCashFlow(new DateTime(2010, 10, 1))
			{
				Items = items
			};

			component.CalcBalances(monthCashFlow, new DateTime(2010, 10, 31), 500);

			items[0].Balance.Should().Equal(220d);
			items[1].Balance.Should().Equal(230d);
			items[2].Balance.Should().Equal(250d);
			items[3].Balance.Should().Equal(280d);
			items[4].Balance.Should().Equal(320d);
			items[5].Balance.Should().Equal(370d);
			items[6].Balance.Should().Equal(430d);
			monthCashFlow.BeginingBalance.Should().Equal(220d);
			monthCashFlow.EndingBalance.Should().Equal(500d);
		}

		private static List<CashFlowEvent> GetCashFlowItems()
		{
			var items = new List<CashFlowEvent>
			{
				new CashFlowEvent {Date = new DateTime(2010, 10, 1), Bill = new Bill{Amount = 10} },
				new CashFlowEvent {Date = new DateTime(2010, 10, 2), Bill = new Bill{Amount = 20} },
				new CashFlowEvent {Date = new DateTime(2010, 10, 5), Bill = new Bill{Amount = 30} },
				new CashFlowEvent {Date = new DateTime(2010, 10, 6), Bill = new Bill{Amount = 40} },
				new CashFlowEvent {Date = new DateTime(2010, 10, 10), Bill = new Bill{Amount = 50} },
				new CashFlowEvent {Date = new DateTime(2010, 10, 14), Bill = new Bill{Amount = 60} },
				new CashFlowEvent {Date = new DateTime(2010, 10, 15), Bill = new Bill{Amount = 70} }
			};
			return items;
		}

		private List<RecurrenceBase> GetRecurrences()
		{
			var fakeRec1 = new Mock<RecurrenceBase>();
			fakeRec1.Setup(x => x.GetNextDates(It.IsAny<DateRange>())).
				Returns(new List<DateTime> {new DateTime(2010, 10, 8), new DateTime(2010, 10, 9)});
			var fakeRec2 = new Mock<RecurrenceBase>();
			fakeRec2.Setup(x => x.GetNextDates(It.IsAny<DateRange>())).
				Returns(new List<DateTime> { new DateTime(2010, 10, 11)});
			var fakeRec3 = new Mock<RecurrenceBase>();
			fakeRec3.Setup(x => x.GetNextDates(It.IsAny<DateRange>())).
				Returns(new List<DateTime> { new DateTime(2010, 10, 13), new DateTime(2010, 10, 14) });
			var fakeRec4 = new Mock<RecurrenceBase>();
			fakeRec4.Setup(x => x.GetNextDates(It.IsAny<DateRange>())).
				Returns(new List<DateTime> { new DateTime(2010, 10, 20), new DateTime(2010, 10, 21) });
			var fakeRec5 = new Mock<RecurrenceBase>();
			fakeRec5.Setup(x => x.GetNextDates(It.IsAny<DateRange>())).
				Returns(new List<DateTime> { new DateTime(2010, 10, 25)});

			return new List<RecurrenceBase> { 
				fakeRec1.Object, 
				fakeRec2.Object, 
				fakeRec3.Object, 
				fakeRec4.Object, 
				fakeRec5.Object 
			};
		}
	}
}
